package org.example.spring.simple.framework.webmvc.servlet;

import lombok.extern.slf4j.Slf4j;
import org.example.spring.simple.framework.annotation.ZController;
import org.example.spring.simple.framework.annotation.ZRequestMapping;
import org.example.spring.simple.framework.context.ZApplicationContext;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class ZDispatcherServlet extends HttpServlet {
    private static final String LOCATION = "contextConfigLocation";
    
    //保存Controller中所有Mapping的对应关系
    private List<ZHandlerMapping> handlerMappings = new ArrayList<>();
    private Map<ZHandlerMapping, ZHandlerAdapter> handlerAdapters = new HashMap<>();
    private ZApplicationContext context;
    private List<ZViewResolver> viewResolvers = new ArrayList<>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        //初始化IoC容器
        context = new ZApplicationContext(config.getInitParameter(LOCATION));
        initStrategies(context);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try{
            this.doDispatch(req,resp);
        }catch(Exception e){
            resp.getWriter().write("500 Exception,Details:\r\n" + Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]", "").replaceAll(",\\s", "\r\n"));
            e.printStackTrace();
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) {
        //1、通过从request中拿到URL，去匹配一个HandlerMapping
        ZHandlerMapping handlerMapping = getHandler(req);
    }

    private ZHandlerMapping getHandler(HttpServletRequest req) {
        if (this.handlerMappings.isEmpty()){
            return null;
        }
        String uri = req.getRequestURI();
        String contextPath = req.getContextPath();
        uri.replace(contextPath, "").replaceAll("/+", "/");
        for (ZHandlerMapping handler : this.handlerMappings) {
            Matcher matcher = handler.getPattern().matcher(uri);
            if (matcher.matches()) {
                return handler;
            }
        }
        return null;
    }

    /**
     * 策略模式，共九种策略
     * 针对每个用户请求都会经过一些策略处理，最终输出结果
     * 无论经过何种处理最终结果一致
     * @param context
     */
    protected void initStrategies(ZApplicationContext context) {
        //mvc九大组件
        //多文件上传的组件
        initMultipartResolver(context);
        //初始化本地语言环境
        initLocaleResolver(context);
        //初始化模板处理器
        initThemeResolver(context);

        //handlerMapping，必须实现
        initHandlerMappings(context);
        //初始化参数适配器，必须实现
        initHandlerAdapters(context);
        //初始化异常拦截器
        initHandlerExceptionResolvers(context);
        //初始化视图预处理器
        initRequestToViewNameTranslator(context);

        //初始化视图转换器，必须实现
        initViewResolvers(context);
        //参数缓存器
        initFlashMapManager(context);
    }

    /**
     * handlerMapping用来保存Controller中配置的RequestMapping和Method的对应关系
     * 通过HandlerMapping将请求映射到处理器
     * @param context
     */
    private void initHandlerMappings(ZApplicationContext context) {
        //从容器中获取所有实例
        String[] beanNames = context.getBeanDefinitionNames();
        try {
            //扫描所有bean，获取controller
            for(String beanName : beanNames) {
                Object controller = context.getBean(beanName);
                Class<?> controllerClass = controller.getClass();
                if (!controllerClass.isAnnotationPresent(ZController.class)) {
                    continue;
                }
                String baseUrl = "";
                if (controllerClass.isAnnotationPresent(ZRequestMapping.class)) {
                    ZRequestMapping requestMapping = controllerClass.getAnnotation(ZRequestMapping.class);
                    baseUrl = requestMapping.value();
                }

                //扫描所有public方法
                Method[] methods = controllerClass.getMethods();
                for (Method method : methods) {
                    if (!method.isAnnotationPresent(ZRequestMapping.class)) {
                        continue;
                    }
                    ZRequestMapping requestMapping = method.getAnnotation(ZRequestMapping.class);
                    String regex = ("/" + baseUrl + requestMapping.value().replaceAll("\\*", ".*")).replaceAll("/+", "/");
                    Pattern pattern = Pattern.compile(regex);
                    this.handlerMappings.add(new ZHandlerMapping(pattern,controller,method));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * handlerAdapter用于动态匹配参数，包括类型转换，动态赋值
     * 通过handlerAdapter进行多类型参数的动态匹配
     * @param context
     */
    private void initHandlerAdapters(ZApplicationContext context) {
        //初始化阶段将参数的名称或类型按一定顺序保存下来，因为后面返回时，传的参数是数组，可以通过下标匹配
        for (ZHandlerMapping handlerMapping : this.handlerMappings) {
            this.handlerAdapters.put(handlerMapping, new ZHandlerAdapter());
        }
    }

    /**
     * 根据不同的请求选择不同的模板引擎来进 行页面的渲染。
     * @param context
     */
    private void initViewResolvers(ZApplicationContext context) {
        //拿到模板的存放目录
        String templateRoot = context.getConfig().getProperty("templateRoot");
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();

        File templateRootDir = new File(templateRootPath);
        String[] templates = templateRootDir.list();
        for (int i = 0; i < templates.length; i ++) {
            //这里主要是为了兼容多模板，所有模仿Spring用List保存
            //在我写的代码中简化了，其实只有需要一个模板就可以搞定
            //只是为了仿真，所有还是搞了个List
            this.viewResolvers.add(new ZViewResolver(templateRoot));
        }
    }

    private void initFlashMapManager(ZApplicationContext context) { }

    private void initRequestToViewNameTranslator(ZApplicationContext context) { }

    private void initHandlerExceptionResolvers(ZApplicationContext context) { }

    private void initThemeResolver(ZApplicationContext context) { }

    private void initLocaleResolver(ZApplicationContext context) { }

    private void initMultipartResolver(ZApplicationContext context) { }


}
